#include "thread_synchronization.h"
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/with_lock_guard.hpp>
//#include <boost/date_time/time.hpp>
#include <boost/date_time.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/thread/latch.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/loop_executor.hpp>
#include <boost/thread/executors/serial_executor.hpp>
#include <boost/thread/executors/thread_executor.hpp>
#include <boost/bind.hpp>
#include <boost/thread/executors/inline_executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/future.hpp>



thread_synchronization::thread_synchronization()
{

}


///***********************************************************************///
///
/// \brief The BankAccount_1 class
///
class BankAccount_1 {
    int balance_;
public:
    BankAccount_1(int balance)
    {
        balance_ = balance;
    }
    void Deposit(int amount) {
        balance_ += amount;
    }
    void Withdraw(int amount) {
        balance_ -= amount;
    }
    int GetBalance() {
        int b = balance_;
        return b;
    }
};

BankAccount_1 ba_1(100000);

///
/// \brief deposit_func_1
///
void deposit_func_1()
{
    for(int i=0; i<10000; i++)
    {
        ba_1.Deposit(i);
        std::cout << "deposit = " << ba_1.GetBalance() << std::endl;
    }
    std::cout << "deposit finished" << std::endl;
}

///
/// \brief withdraw_func_1
///
void withdraw_func_1()
{
    for(int i=0; i<10000; i++)
    {
        ba_1.Withdraw(i);
        std::cout << "withdraw = " << ba_1.GetBalance() << std::endl;
    }
    std::cout << "withdraw finished" << std::endl;
}

///
/// \brief getbalance_1
///
void getbalance_1()
{
    for(int i=0; i<10000; i++)
        std::cout << "rest balance = " << ba_1.GetBalance() << std::endl;
    std::cout << "getbalance finished" << std::endl;
}

///
/// \brief thread_synchronization::test_without_lock
///
void thread_synchronization::test_without_lock()
{
    boost::thread deposit(&deposit_func_1);
    boost::thread withdraw(&withdraw_func_1);
    boost::thread getbalance(&getbalance_1);

    deposit.join();
    withdraw.join();
    getbalance.join();

    //boost::this_thread::sleep_for(boost::chrono::seconds(5));
    std::cout << "finally balance is " << ba_1.GetBalance() << std::endl;
    std::cout << "main thread finished" << std::endl;
}


///*******************************************************************************///
///
/// \brief The BankAccount_2 class
///
class BankAccount_2 {
    boost::mutex mtx_;
    int balance_;
public:
    BankAccount_2(int balance)
    {
        balance_ = balance;
    }
    void Deposit(int amount) {
        mtx_.lock();
        balance_ += amount;
        mtx_.unlock();
    }
    void Withdraw(int amount) {
        mtx_.lock();
        balance_ -= amount;
        mtx_.unlock();
    }
    int GetBalance() {
        mtx_.lock();
        int b = balance_;
        mtx_.unlock();
        return b;
    }
};

BankAccount_2 ba_2(100000);

///
/// \brief deposit_func_2
///
void deposit_func_2()
{
    for(int i=0; i<10000; i++)
    {
        ba_2.Deposit(i);
        std::cout << "deposit = " << ba_2.GetBalance() << std::endl;
    }
    std::cout << "deposit finished" << std::endl;
}

///
/// \brief withdraw_func_2
///
void withdraw_func_2()
{
    for(int i=0; i<10000; i++)
    {
        ba_2.Withdraw(i);
        std::cout << "withdraw = " << ba_2.GetBalance() << std::endl;
    }
    std::cout << "withdraw finished" << std::endl;
}

///
/// \brief getbalance_2
///
void getbalance_2()
{
    for(int i=0; i<10000; i++)
        std::cout << "rest balance = " << ba_2.GetBalance() << std::endl;
    std::cout << "getbalance finished" << std::endl;
}

///
/// \brief thread_synchronization::test_internal_lock
///
void thread_synchronization::test_internal_lock()
{
    boost::thread deposit(&deposit_func_2);
    boost::thread withdraw(&withdraw_func_2);
    boost::thread getbalance(&getbalance_2);

    deposit.join();
    withdraw.join();
    getbalance.join();
    //boost::this_thread::sleep_for(boost::chrono::seconds(30));
    std::cout << "finally balance is " << ba_2.GetBalance() << std::endl;
    std::cout << "main thread finished" << std::endl;
}

///**************************************************************************************///
///
/// \brief The BankAccount_3 class
///
class BankAccount_3 {
    boost::mutex mtx_; // explicit mutex declaration
    int balance_;
public:
    BankAccount_3(int balance)
    {
        balance_ = balance;
    }
    void Deposit(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ -= amount;
    }
    int GetBalance() {
        boost::lock_guard<boost::mutex> guard(mtx_);
        return balance_;
    }
};

BankAccount_3 ba_3(100000);

///
/// \brief deposit_func_3
///
void deposit_func_3()
{
    for(int i=0; i<10000; i++)
    {
        ba_3.Deposit(i);
        std::cout << "deposit = " << ba_3.GetBalance() << std::endl;
    }
    std::cout << "deposit finished" << std::endl;
}

///
/// \brief withdraw_func_3
///
void withdraw_func_3()
{
    for(int i=0; i<10000; i++)
    {
        ba_3.Withdraw(i);
        std::cout << "withdraw = " << ba_3.GetBalance() << std::endl;
    }
    std::cout << "withdraw finished" << std::endl;
}

///
/// \brief getbalance_3
///
void getbalance_3()
{
    for(int i=0; i<10000; i++)
        std::cout << "rest balance = " << ba_3.GetBalance() << std::endl;
    std::cout << "getbalance finished" << std::endl;
}

///
/// \brief thread_synchronization::test_external_lock
///
void thread_synchronization::test_external_lock()
{
    boost::thread deposit(&deposit_func_3);
    boost::thread withdraw(&withdraw_func_3);
    boost::thread getbalance(&getbalance_3);

    deposit.join();
    withdraw.join();
    getbalance.join();

    //boost::this_thread::sleep_for(boost::chrono::seconds(30));
    std::cout << "finally balance is " << ba_3.GetBalance() << std::endl;
    std::cout << "main thread finished" << std::endl;
}

///*******************************************************************************************///

///
/// \brief The BankAccount_4 class
///
class BankAccount_4 : public boost::mutex{
    int balance_;
public:
    BankAccount_4(int balance)
    {
        balance_ = balance;
    }
    void Deposit(int amount) {
        boost::lock_guard<boost::mutex> guard(*this);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<boost::mutex> guard(*this);
        balance_ -= amount;
    }
    int GetBalance() {
        boost::lock_guard<boost::mutex> guard(*this);
        int b = balance_;
        return b;
    }
};

BankAccount_4 ba_4(100000);

///
/// \brief The BankAccount_5 class
///
class BankAccount_5 : public boost::recursive_mutex{
    boost::recursive_mutex mtx_;
    int balance_;
public:
    BankAccount_5(int balance)
    {
        balance_ = balance;
    }
    void Deposit(int amount) {
        boost::lock_guard<boost::recursive_mutex> guard(*this);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<boost::recursive_mutex> guard(*this);
        balance_ -= amount;
    }
    int GetBalance() {
        boost::lock_guard<boost::recursive_mutex> guard(*this);
        int b = balance_;
        return b;
    }
};
BankAccount_5 ba_5(100000);

///
/// \brief mutex_func_1
///
void mutex_func_1()
{
    boost::lock_guard<boost::mutex> guard(ba_4);
    ba_4.Deposit(10);
    std::cout << "balance = " << ba_4.GetBalance() << std::endl;
}

///
/// \brief recursive_func_1
///
void recursive_func_1()
{
    boost::lock_guard<boost::recursive_mutex> guard(ba_5);
    ba_5.Deposit(10);
    std::cout << "balance = " << ba_5.GetBalance() << std::endl;
}


///
/// \brief thread_synchronization::test_recursive_lock
///
void thread_synchronization::test_recursive_lock()
{
    boost::thread(&mutex_func_1).join();
    boost::thread(&recursive_func_1).join();
    std::cout << "main thread finished" << std::endl;
}

///**************************************************************************************///

int g_value = 0;

///
/// \brief add_value
///
void add_value()
{
    for(int i=0; i<10000; i++)
    {
        g_value += i;
        boost::this_thread::sleep_for(boost::chrono::microseconds(5));
    }
}


///
/// \brief lock_add_value
///
void lock_add_value(boost::mutex &mutex)
{
    boost::with_lock_guard(mutex, &add_value);
}

///
/// \brief thread_synchronization::test_lock_function
///
void thread_synchronization::test_lock_function()
{
    g_value = 0;
    boost::thread add_thread_1(&add_value);
    boost::thread add_thread_2(&add_value);

    add_thread_1.join();
    add_thread_2.join();

    std::cout << "g_value_1 = " << g_value << std::endl;


    g_value = 0;
    boost::thread add_thread_3(&add_value);
    boost::thread add_thread_4(&add_value);

    add_thread_3.join();
    add_thread_4.join();

    std::cout << "g_value_2 = " << g_value << std::endl;


    boost::mutex mutex;
    g_value = 0;
    boost::thread add_thread_5(&lock_add_value, boost::ref(mutex));
    boost::thread add_thread_6(&lock_add_value, boost::ref(mutex));

    add_thread_5.join();
    add_thread_6.join();

    std::cout << "g_value_3 = " << g_value << std::endl;


    g_value = 0;
    boost::thread add_thread_7(&lock_add_value, boost::ref(mutex));
    boost::thread add_thread_8(&lock_add_value, boost::ref(mutex));

    add_thread_7.join();
    add_thread_8.join();

    std::cout << "g_value_4 = " << g_value << std::endl;
}

///***********************************************************************************///

boost::mutex mutex;

void try_lock_func()
{
    mutex.try_lock();
    boost::this_thread::sleep_for(boost::chrono::seconds(5));
    mutex.unlock();
}

///
/// \brief thread_synchronization::test_try_lock
///
void thread_synchronization::test_try_lock()
{
    boost::thread try_lock_thread(&try_lock_func);

    boost::this_thread::sleep_for(boost::chrono::seconds(1));

    if(mutex.try_lock())
    {
        std::cout << "get lock success" << std::endl;
    }else
    {
        std::cout << "get lock failed" << std::endl;
    }

    if(mutex.try_lock_for(boost::chrono::seconds(1)))
    {
        std::cout << "sleep for 1 seconds and get lock success" << std::endl;
    }else
    {
        std::cout << "sleep for 1 seconds and get lock failed" << std::endl;
    }

    if(mutex.try_lock_until(boost::chrono::system_clock::now() + boost::chrono::seconds(1)))
    {
        std::cout << "sleep until 1 seconds later and get lock success" << std::endl;
    }else
    {
        std::cout << "sleep until 1 seconds later and get lock failed" << std::endl;
    }
}

///*********************************************************************************************///

boost::mutex time_mutex;
///
/// \brief time_lock_func
///
void time_lock_func()
{

    time_mutex.timed_lock(boost::posix_time::seconds(3));
    std::cout << "time_lock_func finished" << std::endl;
    time_mutex.unlock();
}

///
/// \brief thread_synchronization::test_timed_lock
///
void thread_synchronization::test_timed_lock()
{
    boost::thread time_thread(&time_lock_func);

    boost::this_thread::sleep_for(boost::chrono::seconds(1));

    if(time_mutex.try_lock())
    {
        std::cout << "one seconds later,first time get lock success" << std::endl;
    }else
    {
        std::cout << "one seconds later,first time get lock failed" << std::endl;
    }

    boost::this_thread::sleep_for(boost::chrono::seconds(3));

    if(time_mutex.try_lock())
    {
        std::cout << "three seconds later,second time get lock success" << std::endl;
    }else
    {
        std::cout << "three seconds later,second time get lock failed" << std::endl;
    }
}

///******************************************************************************************///


boost::shared_mutex shared_mutex;

///
/// \brief shared_mutex_func_1
///
void shared_mutex_func_1()
{
    shared_mutex.lock();
    boost::this_thread::sleep_for(boost::chrono::seconds(3));
    shared_mutex.unlock();
}

///
/// \brief shared_mutex_func_2
///
void shared_mutex_func_2()
{
    shared_mutex.lock_shared();
    boost::this_thread::sleep_for(boost::chrono::seconds(3));
    shared_mutex.unlock_shared();
}


///
/// \brief thread_synchronization::test_shared_lock
///
void thread_synchronization::test_shared_lock()
{
//    boost::thread shared_mutex_thread_1(&shared_mutex_func_1);

//    boost::this_thread::sleep_for(boost::chrono::seconds(1));

//    if(shared_mutex.try_lock_shared())
//    {
//        std::cout << "get shared success" << std::endl;
//    }else
//    {
//        std::cout << "get shared failed" << std::endl;
//    }


    boost::thread shared_mutex_thread_1(&shared_mutex_func_2);

    boost::this_thread::sleep_for(boost::chrono::seconds(1));

    if(shared_mutex.try_lock())
    {
        std::cout << "get shared success" << std::endl;
    }else
    {
        std::cout << "get shared failed" << std::endl;
    }


//    boost::thread shared_mutex_thread_2(&shared_mutex_func_2);

//    boost::this_thread::sleep_for(boost::chrono::seconds(1));

//    if(shared_mutex.try_lock_shared())
//    {
//        std::cout << "get shared success" << std::endl;
//    }else
//    {
//        std::cout << "get shared failed" << std::endl;
    //    }
}


///**********************************************************************************///

boost::upgrade_mutex upgrade_mutex;

void upgrade_mutex_func()
{
    upgrade_mutex.lock_shared();
    boost::this_thread::sleep_for(boost::chrono::seconds(3));
    upgrade_mutex.unlock_shared();
}

///
/// \brief thread_synchronization::test_upgrade_lock
///
void thread_synchronization::test_upgrade_lock()
{
    boost::thread upgrade_thread(&upgrade_mutex_func);

    boost::this_thread::sleep_for(boost::chrono::seconds(1));


    if(upgrade_mutex.try_lock_upgrade())
    {
        std::cout << "first get shared lock success" << std::endl;
    }else
    {
        std::cout << "first get shared lock failed" << std::endl;
    }

    if(upgrade_mutex.try_lock_shared())
    {
        std::cout << "second get shared lock success" << std::endl;
    }else
    {
        std::cout << "second get shared lock failed" << std::endl;
    }

}

///****************************************************************************************///

boost::mutex lock_mutex;
///
/// \brief unique_lock_func_1
///
void unique_lock_func_1()
{
    boost::unique_lock<boost::mutex> unique_lock(lock_mutex);
    boost::this_thread::sleep_for(boost::chrono::milliseconds(3000));
    std::cout << "unique lock thread finished" <<std::endl;
}

///
/// \brief thread_synchronization::test_unique_lock
///

void thread_synchronization::test_unique_lock()
{
    boost::thread unique_thread_1(&unique_lock_func_1);

    //boost::this_thread::sleep_for(boost::chrono::seconds(1));

    boost::thread unique_thread_2(&unique_lock_func_1);

    unique_thread_1.join();
    unique_thread_2.join();

}

///************************************************************************************///

boost::shared_mutex shared_lock_mutex;

///
/// \brief shared_lock_func_1
///
void shared_lock_func_1()
{
    boost::shared_lock<boost::shared_mutex> shared_lock(shared_lock_mutex);
    boost::this_thread::sleep_for(boost::chrono::milliseconds(3000));
    std::cout << "shared lock thread finished" <<std::endl;
}

///
/// \brief thread_synchronization::test_shared_lock_1
///

void thread_synchronization::test_shared_lock_1()
{
    boost::thread shared_thread_1(&shared_lock_func_1);

    boost::thread shared_thread_2(&shared_lock_func_1);

    shared_thread_1.join();
    shared_thread_2.join();

}

///***************************************************************************************///

boost::condition_variable cond;
boost::mutex mut;
bool data_ready = false;

void process_data();

void wait_for_data_to_process()
{
    boost::unique_lock<boost::mutex> lock(mut);
    std::cout << "get lock success " << std::endl;
    while(!data_ready)
    {
        std::cout << "waiting notifing" << std::endl;
        cond.wait(lock);
    }


    std::cout << "do something" << std::endl;
}

///
/// \brief prepare_data_for_processing
///
void prepare_data_for_processing()
{
    boost::this_thread::sleep_for(boost::chrono::seconds(3));
    {
        boost::lock_guard<boost::mutex> lock(mut);
        data_ready=true;
        std::cout << "data ready..." << std::endl;
    }
    cond.notify_all();
}

///
/// \brief thread_synchronization::test_condition_variable
///

void thread_synchronization::test_condition_variable()
{
    boost::thread cond_thread_1(&wait_for_data_to_process);
    boost::thread cond_thread_2(&prepare_data_for_processing);
    boost::thread cond_thread_3(&wait_for_data_to_process);

    cond_thread_1.join();
    cond_thread_2.join();
    cond_thread_3.join();
}

///*********************************************************************************************///

boost::once_flag f=BOOST_ONCE_INIT;
///
/// \brief call_one
///
void call_one()
{
    std::cout << "func called" << std::endl;
}

void call_one_func()
{
    boost::call_once(f,&call_one);
    std::cout << "call one func called" << std::endl;
}
///
/// \brief thread_synchronization::test_call_one
///

void thread_synchronization::test_call_one()
{
    boost::thread(&call_one_func).join();
    boost::thread(&call_one_func).join();

    call_one_func();
    call_one_func();
}

///**************************************************************************************************///

boost::barrier barrier(3);

///
/// \brief barrier_func
///
void barrier_func()
{
    std::cout << "come in barrier func" << std::endl;
    boost::this_thread::sleep_for(boost::chrono::seconds(1));
    barrier.wait();
    std::cout << "barrier func" << std::endl;
}
///
/// \brief thread_synchronization::test_barrier
///
void thread_synchronization::test_barrier()
{
    boost::thread th_1(&barrier_func);
    boost::thread th_2(&barrier_func);

    boost::this_thread::sleep_for(boost::chrono::seconds(4));
    boost::thread th_3(&barrier_func);
    th_3.join();
    th_1.join();
    th_2.join();
}

///*******************************************************************************************************///

boost::latch latch(3);

void latch_func(int i)
{
    std::cout << "do something" << std::endl;
    boost::this_thread::sleep_for(boost::chrono::seconds(i));
    latch.count_down();
    std::cout << "latch_func " << i << " exit"<<std::endl;
}

boost::latch latch_1(1);

void latch_func_1(int i)
{
    std::cout << "do something" << std::endl;
    boost::this_thread::sleep_for(boost::chrono::seconds(i));
    std::cout << "wait other tasks to be done" << std::endl;
    latch_1.wait();
    std::cout << i << ": do the job" << std::endl;
    boost::this_thread::sleep_for(boost::chrono::seconds(i));
    std::cout << i << ": job has been done" << std::endl;

    std::cout << "latch_func " << i << " exit"<<std::endl;
}
///
/// \brief thread_synchronization::test_latch
///
void thread_synchronization::test_latch()
{
//    boost::thread latch_thread_1(&latch_func, 1);
//    boost::thread latch_thread_2(&latch_func, 2);
//    boost::thread latch_thread_3(&latch_func, 3);

//    latch.wait();

//    std::cout << "something has been done" << std::endl;

//    latch_thread_1.join();
//    latch_thread_2.join();
//    latch_thread_3.join();

    boost::thread latch_thread_1(&latch_func_1, 1);
    boost::thread latch_thread_2(&latch_func_1, 2);
    boost::thread latch_thread_3(&latch_func_1, 3);

    std::cout << "do something others ..." << std::endl;
    boost::this_thread::sleep_for(boost::chrono::seconds(10));
    std::cout << "other something has been done  ..." << std::endl;

    latch_1.count_down();

    std::cout << "something has been done" << std::endl;

    latch_thread_1.join();
    latch_thread_2.join();
    latch_thread_3.join();

}

///*********************************************************************************************************///

///
/// \brief task_func
///
void task_func(int i)
{
    boost::this_thread::sleep_for(boost::chrono::seconds(1));
    std::cout << i << ": do some tasks..." << std::endl;
}

///
/// \brief thread_synchronization::test_exector
///

void thread_synchronization::test_exector()
{
//    boost::basic_thread_pool btp;
//        for(int i=0; i<10; i++)
//            btp.submit(boost::bind(&task_func, i));

//    boost::executors::executor_adaptor<boost::basic_thread_pool> ee;
//    boost::serial_executor se(ee);
//    for(int i=0; i<10; i++)
//        se.submit(boost::bind(&task_func, i));

//    boost::thread_executor te;
//    for(int i=0; i<10; i++)
//        te.submit(boost::bind(&task_func, i));

//    boost::this_thread::sleep_for(boost::chrono::seconds(3));

//    boost::inline_executor ie;
//    for(int i=0; i<10; i++)
//        ie.submit(boost::bind(&task_func, i));

//    boost::loop_executor le;
//    for(int i=0; i<10; i++)
//        le.submit(boost::bind(&task_func, i));

//    le.loop();

    std::cout << "main thread done..." << std::endl;
}

///****************************************************************************************************///

///
/// \brief async
/// \param i
/// \return
///
int async(int i)
{
    std::cout << "return " << i << std::endl;
    return i;
}

///
/// \brief thread_synchronization::test_future
///

void thread_synchronization::test_future()
{

}
